/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010-2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORE_SPELLAURAEFFECTS_H
#define ARKCORE_SPELLAURAEFFECTS_H

class Unit;
class DynamicObject;
class AuraEffect;
class Aura;

#include "SpellAuras.h"

typedef void(AuraEffect::*pAuraEffectHandler)(AuraApplication const * aurApp,
		uint8 mode, bool apply) const;

class AuraEffect {
	friend void Aura::_InitEffects(uint8 effMask, Unit * caster,
			int32 *baseAmount);friend Aura::~Aura();
private:
	~AuraEffect();
	explicit AuraEffect(Aura * base, uint8 effIndex, int32 *baseAmount,
			Unit * caster);
public:
	Unit * GetCaster() const {
		return GetBase()->GetCaster();
	}
	uint64 GetCasterGUID() const {
		return GetBase()->GetCasterGUID();
	}
	Aura * GetBase() const {
		return m_base;
	}
	void GetTargetList(std::list<Unit*> & targetList) const;
	void GetApplicationList(
			std::list<AuraApplication*> & applicationList) const;

	SpellEntry const * GetSpellProto() const {
		return m_spellProto;
	}
	uint32 GetId() const {
		return m_spellProto->Id;
	}
	uint32 GetEffIndex() const {
		return m_effIndex;
	}
	int32 GetBaseAmount() const {
		return m_baseAmount;
	}
	int32 GetAmplitude() const {
		return m_amplitude;
	}

	int32 GetMiscValueB() const {
		return m_spellProto->EffectMiscValueB[m_effIndex];
	}
	int32 GetMiscValue() const {
		return m_spellProto->EffectMiscValue[m_effIndex];
	}
	AuraType GetAuraType() const {
		return (AuraType) m_spellProto->EffectApplyAuraName[m_effIndex];
	}
	int32 GetAmount() const {
		return m_amount;
	}
	void SetAmount(int32 amount) {
		m_amount = amount;
		m_canBeRecalculated = false;
	}

	int32 GetPeriodicTimer() const {
		return m_periodicTimer;
	}
	void SetPeriodicTimer(int32 periodicTimer) {
		m_periodicTimer = periodicTimer;
	}

	int32 CalculateAmount(Unit * caster);
	void CalculatePeriodic(Unit * caster, bool create = false);
	void CalculateSpellMod();
	void ChangeAmount(int32 newAmount, bool mark = true);
	void RecalculateAmount() {
		if (!CanBeRecalculated())
			return;
		ChangeAmount(CalculateAmount(GetCaster()), false);
	}
	void RecalculateAmount(Unit * caster) {
		if (!CanBeRecalculated())
			return;
		ChangeAmount(CalculateAmount(caster), false);
	}
	bool CanBeRecalculated() const {
		return m_canBeRecalculated;
	}
	void SetCanBeRecalculated(bool val) {
		m_canBeRecalculated = val;
	}
	void HandleEffect(AuraApplication const * aurApp, uint8 mode, bool apply);
	void HandleEffect(Unit * target, uint8 mode, bool apply);
	void ApplySpellMod(Unit * target, bool apply);

	void Update(uint32 diff, Unit * caster);
	void UpdatePeriodic(Unit * caster);

	uint32 GetTickNumber() const {
		return m_tickNumber;
	}
	int32 GetTotalTicks() const {
		return m_amplitude ? (GetBase()->GetMaxDuration() / m_amplitude) : 1;
	}
	void ResetPeriodic(bool resetPeriodicTimer = false) {
		if (resetPeriodicTimer)
			m_periodicTimer = m_amplitude;
		m_tickNumber = 0;
	}

	bool IsPeriodic() const {
		return m_isPeriodic;
	}
	void SetPeriodic(bool isPeriodic) {
		m_isPeriodic = isPeriodic;
	}
	bool IsAffectedOnSpell(SpellEntry const *spell) const;

	void SendTickImmune(Unit * target, Unit *caster) const;

	void PeriodicTick(AuraApplication * aurApp, Unit * caster) const;
	void PeriodicDummyTick(Unit * target, Unit * caster) const;
	Unit* GetTriggerTarget(Unit * target) const;
	void TriggerSpell(Unit * target, Unit * caster) const;
	void TriggerSpellWithValue(Unit * target, Unit * caster) const;

	void CleanupTriggeredSpells(Unit * target);

	// add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras
	void HandleShapeshiftBoosts(Unit * target, bool apply) const;
private:
	Aura * const m_base;

	SpellEntry const * const m_spellProto;
	uint8 const m_effIndex;
	int32 const m_baseAmount;

	int32 m_amount;
	bool m_canBeRecalculated;

	SpellModifier *m_spellmod;

	bool m_isPeriodic;
	int32 m_periodicTimer;
	int32 m_amplitude;
	uint32 m_tickNumber;
private:
	bool IsPeriodicTickCrit(Unit * target, Unit const * caster) const;

public:
	// aura effect handlers
	void HandleNULL(AuraApplication const * /*aurApp*/, uint8 /*mode*/,
			bool /*apply*/) const {
		// not implemented
	}
	void HandleUnused(AuraApplication const * /*aurApp*/, uint8 /*mode*/,
			bool /*apply*/) const {
		// useless
	}
	void HandleNoImmediateEffect(AuraApplication const * /*aurApp*/,
			uint8 /*mode*/, bool /*apply*/) const {
		// aura type not have immediate effect at add/remove and handled by ID in other code place
	}
	//  visibility & phases
	void HandleModInvisibilityDetect(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModInvisibility(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModTrapLauncher(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraSaleForGuild(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModCamouflage(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModStealth(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModStealthLevel(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModStealthDetect(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleSpiritOfRedemption(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraGhost(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandlePhase(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  unit model
	void HandleAuraModShapeshift(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraTransform(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModScale(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraCloneCaster(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  fight
	void HandleFeignDeath(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModUnattackable(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModDisarm(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModSilence(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModPacify(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModPacifyAndSilence(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraAllowOnlyAbility(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  tracking
	void HandleAuraTrackResources(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraTrackCreatures(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraTrackStealthed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModStalked(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraUntrackable(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  skills & talents
	void HandleAuraModPetTalentsPoints(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModSkill(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  movement
	void HandleAuraMounted(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraAllowFlight(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraWaterWalk(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraFeatherFall(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraHover(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleWaterBreathing(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleForceMoveForward(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  threat
	void HandleModThreat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModTotalThreat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModTaunt(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  control
	void HandleModConfuse(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModFear(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModStun(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModRoot(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandlePreventFleeing(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  charm
	void HandleModPossess(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModPossessPet(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModCharm(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleCharmConvert(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraControlVehicle(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  modify speed
	void HandleAuraModIncreaseSpeed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModIncreaseMountedSpeed(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModIncreaseFlightSpeed(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModIncreaseSwimSpeed(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModDecreaseSpeed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModUseNormalSpeed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  immunity
	void HandleModStateImmunityMask(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModMechanicImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModEffectImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModStateImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModSchoolImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModDmgImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModDispelImmunity(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  modify stats
	//   resistance
	void HandleAuraModResistanceExclusive(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModResistance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModBaseResistancePCT(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModResistancePercent(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModBaseResistance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModTargetResistance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//    stat
	void HandleAuraModStat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModPercentStat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModSpellDamagePercentFromStat(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModSpellHealingPercentFromStat(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModSpellDamagePercentFromAttackPower(
			AuraApplication const * aurApp, uint8 mode, bool apply) const;
	void HandleModSpellHealingPercentFromAttackPower(
			AuraApplication const * aurApp, uint8 mode, bool apply) const;
	void HandleModHealingDone(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModSpellPowerPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModTotalPercentStat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModResistenceOfStatPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModExpertise(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//   heal and energize
	void HandleModPowerRegen(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModPowerRegenPCT(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModManaRegen(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModIncreaseHealth(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModIncreaseMaxHealth(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModIncreaseEnergy(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModIncreaseEnergyPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModIncreaseHealthPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraIncreaseBaseHealthPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	//   fight
	void HandleAuraModParryPercent(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModDodgePercent(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModBlockPercent(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModRegenInterrupt(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModWeaponCritPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModHitChance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModSpellHitChance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModSpellCritChance(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModSpellCritChanceShool(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModCritPct(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//   attack speed
	void HandleModCastingSpeed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModMeleeRangedSpeedPct(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleModCombatSpeedPct(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModAttackSpeed(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModMeleeSpeedPct(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModRangedHaste(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleRangedAmmoHaste(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//   combat rating
	void HandleModRating(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModRatingFromStat(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//   attack power
	void HandleAuraModAttackPower(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModRangedAttackPower(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModAttackPowerPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModRangedAttackPowerPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModRangedAttackPowerOfStatPercent(
			AuraApplication const * aurApp, uint8 mode, bool apply) const;
	void HandleAuraModAttackPowerOfStatPercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleAuraModAttackPowerOfArmor(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	//   damage bonus
	void HandleModDamageDone(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModDamagePercentDone(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModOffhandDamagePercent(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
	void HandleShieldBlockValue(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  power cost
	void HandleModPowerCostPCT(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleModPowerCost(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleArenaPreparation(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleNoReagentUseAura(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraRetainComboPoints(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	//  others
	void HandleAuraDummy(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleChannelDeathItem(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleBindSight(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleForceReaction(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraEmpathy(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModFaction(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleComprehendLanguage(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraConvertRune(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraLinked(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraOpenStable(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraOverrideSpells(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraSetVehicle(AuraApplication const * aurApp, uint8 mode,
			bool apply) const;
	void HandleAuraModFakeInebriation(AuraApplication const * aurApp,
			uint8 mode, bool apply) const;
};

namespace Trinity {
// Binary predicate for sorting the priority of absorption aura effects
class AbsorbAuraOrderPred {
public:
	AbsorbAuraOrderPred() {
	}
	bool operator()(AuraEffect * aurEffA, AuraEffect * aurEffB) const {
		SpellEntry const * spellProtoA = aurEffA->GetSpellProto();
		SpellEntry const * spellProtoB = aurEffB->GetSpellProto();

		// Wards
		if ((spellProtoA->SpellFamilyName == SPELLFAMILY_MAGE)
				|| (spellProtoA->SpellFamilyName == SPELLFAMILY_WARLOCK))
			if (spellProtoA->Category == 56)
				return true;
		if ((spellProtoB->SpellFamilyName == SPELLFAMILY_MAGE)
				|| (spellProtoB->SpellFamilyName == SPELLFAMILY_WARLOCK))
			if (spellProtoB->Category == 56)
				return false;

		// Fel Blossom
		if (spellProtoA->Id == 28527)
			return true;
		if (spellProtoB->Id == 28527)
			return false;

		// Divine Aegis
		if (spellProtoA->Id == 47753)
			return true;
		if (spellProtoB->Id == 47753)
			return false;

		// Ice Barrier
		if (spellProtoA->Category == 471)
			return true;
		if (spellProtoB->Category == 471)
			return false;

		// Sacrifice
		if ((spellProtoA->SpellFamilyName == SPELLFAMILY_WARLOCK)
				&& (spellProtoA->SpellIconID == 693))
			return true;
		if ((spellProtoB->SpellFamilyName == SPELLFAMILY_WARLOCK)
				&& (spellProtoB->SpellIconID == 693))
			return false;

		return false;
	}
};
}
#endif
